#![no_std]
#![no_main]
#![feature(abi_efiapi)]
#![feature(lang_items)]
#![feature(start)]
#![feature(allocator_api)]
#![feature(new_uninit)]
#![feature(core_intrinsics)]
#![allow(incomplete_features)]
#![feature(adt_const_params)]

extern crate alloc;

use core::{
    ffi::c_void,
    ptr::{null, null_mut, NonNull},
};

use uefi_ffi::{
    services::boot::{
        event::{EfiEvent, EfiEventNotify, EfiTimerDelay},
        memory::{EfiAllocateType, EfiPhysicalAddress, MemoryType},
        task::EfiTpl,
        EfiBootServices, EfiGuid,
    },
    system_table::EfiSystemTable,
    EfiHandle, EfiStatus,
};
extern "Rust" {
    fn main();
}

pub mod event;
pub mod io;
pub mod mem;
pub mod protocol;
pub mod task;

// 如非特殊需要，则只能通过 BootServices::take() 来获取非 mut 引用。
static mut EFI_BOOT_SERVICES: *mut EfiBootServices = null_mut();
static mut EFI_SYSTEM_TABLE: *const EfiSystemTable = null();
static mut IMAGE_HANDLE: EfiHandle = null_mut();

/// 在调用 efi_main 之前调用
#[no_mangle]
pub fn efi_main(image_handle: EfiHandle, system_table: *const EfiSystemTable) -> EfiStatus {
    unsafe {
        IMAGE_HANDLE = image_handle;
        EFI_SYSTEM_TABLE = system_table;
        EFI_BOOT_SERVICES = (*system_table).boot_services;
        // 应用程序的入口函数，和通常的 rust 程序一样。
        main();
    }
    EfiStatus::Success
}

/// 类型判断
static _MAIN: uefi_ffi::EfiImageEntryPoint = efi_main;

#[cfg(not(test))]
#[lang = "eh_personality"]
#[no_mangle]
pub extern "C" fn rust_eh_personality() {}

#[cfg(not(test))]
#[lang = "panic_impl"]
#[no_mangle]
pub extern "C" fn rust_begin_panic(_info: &core::panic::PanicInfo) -> ! {
    // unsafe { intrinsics::abort() }
    loop {}
}

pub struct BootServices {
    raw: &'static EfiBootServices,
}
impl BootServices {
    pub fn take() -> Self {
        BootServices {
            raw: unsafe { NonNull::new(EFI_BOOT_SERVICES).unwrap().as_ref() },
        }
    }
    pub fn create_event(
        &self,
        evt: u32,
        tpl: EfiTpl,
        notify_fn: EfiEventNotify,
        notify_context: *mut c_void,
        event: *mut EfiEvent,
    ) -> EfiStatus {
        (self.raw.create_event)(evt, tpl, notify_fn, notify_context, event)
    }
    pub fn create_event_ex(
        &self,
        evt: u32,
        tpl: EfiTpl,
        notify_fn: EfiEventNotify,
        notify_context: *mut c_void,
        event_group: *const EfiGuid,
        event: *mut EfiEvent,
    ) -> EfiStatus {
        (self.raw.create_event_ex)(evt, tpl, notify_fn, notify_context, event_group, event)
    }
    pub fn wait_for_event(&self, num: usize, event: *const EfiEvent, idx: *mut usize) -> EfiStatus {
        (self.raw.wait_for_event)(num, event, idx)
    }
    // 只会返回 EFI_SUCESS
    pub fn close_event(&self, event: EfiEvent) -> EfiStatus {
        (self.raw.close_event)(event)
    }
    pub fn signal_event(&self, event: EfiEvent) -> EfiStatus {
        (self.raw.signal_event)(event)
    }
    pub fn check_event(&self, event: EfiEvent) -> EfiStatus {
        (self.raw.check_event)(event)
    }
    pub fn set_timer(
        &self,
        event: EfiEvent,
        timer_type: EfiTimerDelay,
        trigger_time: u64,
    ) -> EfiStatus {
        (self.raw.set_timer)(event, timer_type, trigger_time)
    }
    pub fn allocate_pages(
        &self,
        allocate_type: EfiAllocateType,
        memory_type: MemoryType,
        page_num: usize,
        phy_addr: *mut EfiPhysicalAddress,
    ) -> EfiStatus {
        (self.raw.allocate_pages)(allocate_type, memory_type, page_num, phy_addr)
    }
    pub fn free_pages(&self, phy_addr: EfiPhysicalAddress, page_num: usize) -> EfiStatus {
        (self.raw.free_pages)(phy_addr, page_num)
    }
    pub fn allocate_pool(
        &self,
        pool_type: MemoryType,
        size: usize,
        buffer: *mut *mut c_void,
    ) -> EfiStatus {
        (self.raw.allocate_pool)(pool_type, size, buffer)
    }
    pub fn free_pool(&self, buffer: *mut c_void) -> EfiStatus {
        (self.raw.free_pool)(buffer)
    }
}
